package org.jing.core.util;

import org.jing.core.lang.JingException;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.Map;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2019-01-17 <br>
 */
@SuppressWarnings({ "unused", "WeakerAccess" })
public class StringUtil {
    /**
     * shouldn't be empty. <br>
     */
    public static final int COMPARE_STRING_NOT_NULL = 2;

    public final static int PAD_MODEL_LEFT = 0;

    public final static int PAD_MODEL_RIGHT = 1;

    public final static byte[] BUFFER_SPACE = " ".getBytes();

    public final static byte[] BUFFER_0 = "0".getBytes();

    /**
     * Description: 广义判断字符串是否为空, 为空则返回空字符串. <br>
     * <b>"null"</b>也会视为空字符串 <br>
     *
     * @param string 待检查字符串 <br>
     * @return <br>
     */
    public static String ifWildEmpty(String string) {
        return ifWildEmpty(string, "");
    }

    /**
     * Description: 广义判断字符串是否为空, 为空则返回默认字符串. <br>
     * <b>"null"</b>也会视为空字符串 <br>
     *
     * @param string 待检查字符串 <br>
     * @param defaultString 默认字符串 <br>
     * @return <br>
     */
    public static String ifWildEmpty(String string, String defaultString) {
        return isWildEmpty(string) ? defaultString : string;
    }

    /**
     * Description: 广义判断字符串是否为空. <br>
     * <b>"null"</b>也会视为空字符串 <br>
     *
     * @param string 待检查字符串 <br>
     * @return <br>
     */
    public static boolean isWildEmpty(String string) {
        return isEmpty(string) || "null".equalsIgnoreCase(string);
    }

    /**
     * Description: 广义判断字符串是否为非空. <br>
     * <b>"null"</b>也会视为空字符串 <br>
     *
     * @param string 待检查字符串 <br>
     * @return <br>
     */
    public static boolean isNotWildEmpty(String string) {
        return !isWildEmpty(string);
    }

    /**
     * Description: 判断字符串是否为空, 为空则返回默认字符串. <br>
     *
     * @param string 待检查字符串 <br>
     * @param defaultString 默认字符串 <br>
     * @return <br>
     */
    public static String ifEmpty(String string, String defaultString) {
        return null == string || string.isEmpty() && null != defaultString ? defaultString : string;
    }

    /**
     * Description: 判断字符串是否为空, 为空则返回空字符串. <br>
     *
     * @param string 待检查字符串 <br>
     * @return <br>
     */
    public static String ifEmpty(String string) {
        return ifEmpty(string, "");
    }

    /**
     * Description: 判断字符串是否不为空, 为空则返回默认字符串. <br>
     *
     * @param string 待检查字符串 <br>
     * @param defaultString 默认字符串 <br>
     * @return <br>
     */
    public static String ifNotEmpty(String string, String defaultString) {
        return isEmpty(string) ? string : defaultString;
    }

    /**
     * Description: 判断字符串是否为空 <br>
     *
     * @param string 待检查字符串 <br>
     * @return <br>
     */
    public static boolean isEmpty(String string) {
        return null == string || string.isEmpty();
    }

    /**
     * Description: 判断字符串是否不为空 <br>
     *
     * @param string 待检查字符串 <br>
     * @return <br>
     */
    public static boolean isNotEmpty(String string) {
        return !isEmpty(string);
    }

    /**
     * Description: Repeat String <br>
     *
     * @author bks <br>
     * @param repeatChar <br>
     * @param num <br>
     * @return <br>
     */
    public static String repeat(char repeatChar, int num) {
        return repeat(String.valueOf(repeatChar), num);
    }

    /**
     * Description: Repeat String <br>
     *
     * @author bks <br>
     * @param repeatString <br>
     * @param num <br>
     * @return <br>
     */
    public static String repeat(String repeatString, int num) {
        StringBuilder stbr = new StringBuilder();
        for (int i = 0; i < num; i++) {
            stbr.append(repeatString);
        }
        return stbr.toString();
    }

    /**
     * Description: Cut Or Add. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @param model <br>
     * @param c <br>
     * @return <br>
     */
    public static String pad(String string, int model, char c, int length) {
        if (string.length() > length) {
            if (model == PAD_MODEL_LEFT) {
                return string.substring(string.length() - length);
            }
            else if (model == PAD_MODEL_RIGHT) {
                return string.substring(0, length);
            }
            else {
                return string;
            }
        }
        else {
            if (model == PAD_MODEL_LEFT) {
                return repeat(c, length - string.length()) + string;
            }
            else if (model == PAD_MODEL_RIGHT) {
                return string + repeat(c, length - string.length());
            }
            else {
                return string;
            }
        }
    }

    public static byte[] pad(byte[] buffer, int model, byte[] fill, int length) {
        byte[] retBuffer = new byte[length];
        int a, b, c, d;
        switch (model) {
            case PAD_MODEL_LEFT:
                a = 0;
                b = Math.max(length - buffer.length, 0);
                c = b;
                d = length;
                break;
            case PAD_MODEL_RIGHT:
            default:
                c = 0;
                d = Math.min(length, buffer.length);
                a = d;
                b = length;
                break;
        }
        for (int i$ = a, j$ = 0; i$ < b; i$++, j$++) {
            j$ = j$ == fill.length ? 0 : j$;
            retBuffer[i$] = fill[j$];
        }
        if (d - c >= 0) {
            System.arraycopy(buffer, c, retBuffer, c, d - c);
        }
        return retBuffer;
    }

    public static String getErrorStack(Throwable t) {
        try (StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw)
        ) {
            t.printStackTrace(pw);
            return sw.toString();
        }
        catch (Throwable ignored) {}
        return "";
    }

    /**
     * Description: 以{}替换占位符的字符串. <br>
     *
     * @param origString <br>
     * @param strings <br>
     * @return <br>
     */
    public static String setPlaceHolder(String origString, String... strings) {
        if (null != strings && strings.length > 0) {
            String newString = origString;
            for (String s$ : strings) {
                newString = newString.replaceFirst("\\{}", s$);
            }
            return newString;
        }
        return origString;
    }

    /**
     * 去掉可能有的空格/BOM头. <br>
     *
     * @param xml <br>
     * @return <br>
     */
    public static String preOperation4XML(String xml) {
        int length = xml.length();
        for (int i$ = 0; i$ < length; i$++) {
            if ('<' == xml.charAt(i$)) {
                return xml.substring(i$);
            }
        }
        return xml;
    }

    /**
     * Description: 将字符串里的<b>{}</b>替换为参数 <br>
     *
     * @param content 文本 <br>
     * @param parameters 参数 <br>
     * @return <br>
     */
    public static String mixParameters(String content, Object... parameters) {
        if (null != parameters) {
            content = content.replaceAll("%", "%%");
            content = content.replaceAll("\\{}", "%s");
            content = String.format(content, parameters);
        }
        return content;
    }

    /**
     * Description: Remove All Space[" "], Line["-"] And UnderLine["_"]. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @return <br>
     */
    public static String removeSpaceAndLine(String string) {
        return StringUtil.ifEmpty(string).replaceAll("[\\s\\-_]", "");
    }

    /**
     * Description: 比较字符串. <br>
     * 0: 忽略空格[" "]/横杠["-"]/下划线["_"]. <br>
     * 1: 忽略空格[" "]/横杠["-"]/下划线["_"]/大小写. <br>
     * 2: 最精确的判断. <br>
     * Else: 普通判断. <br>
     *
     * @author bks <br>
     * @param string1 <br>
     * @param string2 <br>
     * @param options <br>
     * @return <br>
     */
    public static boolean compareString(String string1, String string2, int options) {
        switch (options) {
            // ignore Space[" "], Line["-"] And UnderLine["_"].
            case 0:
                return removeSpaceAndLine(string1).equals(removeSpaceAndLine(string2));
            // ignore Space[" "], Line["-"] ,UnderLine["_"] And Case.
            case 1:
                return removeSpaceAndLine(string1).equalsIgnoreCase(removeSpaceAndLine(string2));
            // shouldn't be empty.
            case COMPARE_STRING_NOT_NULL:
                return null != string1 && string1.equals(string2);
            // Common.
            default:
                return StringUtil.ifEmpty(string1).equals(string2);
        }
    }

    /**
     * Description: Transfer String To Byte Without Exception. <br>
     * If failed, it will return 0. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @return <br>
     */
    public static byte parseByte(String string) {
        return parseByte(string, (byte) 0);
    }

    /**
     * Description: Transfer String To Byte Without Exception. <br>
     * If failed, it will return defaultValue. If defaultValue was not given, it will return 0. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @param defaultByte <br>
     * @return <br>
     */
    public static byte parseByte(String string, byte defaultByte) {
        byte retByte = defaultByte;
        try {
            retByte = Byte.parseByte(string);
            return retByte;
        }
        catch (Throwable t) {
            return retByte;
        }
    }

    /**
     * Description: Transfer String To Short Without Exception. <br>
     * If failed, it will return 0. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @return <br>
     */
    public static short parseShort(String string) {
        return parseShort(string, (short) 0);
    }

    /**
     * Description: Transfer String To Short Without Exception. <br>
     * If failed, it will return defaultValue. If defaultValue was not given, it will return 0. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @param defaultShort <br>
     * @return <br>
     */
    public static short parseShort(String string, short defaultShort) {
        short retShort = defaultShort;
        try {
            retShort = Short.parseShort(string);
            return retShort;
        }
        catch (Throwable t) {
            return retShort;
        }
    }

    /**
     * Description: Transfer String To Integer Without Exception. <br>
     * If failed, it will return 0. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @return <br>
     */
    public static int parseInteger(String string) {
        return parseInteger(string, 0);
    }

    /**
     * Description: Transfer String To Integer Without Exception. <br>
     * If failed, it will return defaultValue. If defaultValue was not given, it will return 0. <br>
     *
     * @author bks <br>
     * @param string <br>
     * @param defaultInteger <br>
     * @return <br>
     */
    public static int parseInteger(String string, int defaultInteger) {
        int retInt = defaultInteger;
        try {
            retInt = Integer.parseInt(string);
            return retInt;
        }
        catch (Throwable e) {
            return retInt;
        }
    }

    public static long parseLong(String string) {
        return parseLong(string, 0);
    }

    public static long parseLong(String string, long defaultLong) {
        long retLong = defaultLong;
        try {
            retLong = Long.parseLong(string);
            return retLong;
        }
        catch (Throwable t) {
            return retLong;
        }
    }

    public static float parseFloat(String string) {
        return parseFloat(string, 0);
    }

    public static float parseFloat(String string, float defaultFloat) {
        float retFloat = defaultFloat;
        try {
            retFloat = Float.parseFloat(string);
            return retFloat;
        }
        catch (Throwable t) {
            return retFloat;
        }
    }

    public static double parseDouble(String string) {
        return parseDouble(string, 0);
    }

    public static double parseDouble(String string, double defaultDouble) {
        double retDouble = defaultDouble;
        try {
            retDouble = Double.parseDouble(string);
            return retDouble;
        }
        catch (Throwable t) {
            return retDouble;
        }
    }

    public static String cutString(String origString, int length) {
        origString = ifEmpty(origString);
        if (origString.length() <= length) {
            return origString;
        }
        else {
            return origString.substring(0, length);
        }
    }

    /**
     * Description: 根据键从Map里获取值的字符串表达. <br>
     * 会对结果进行ifEmpty处理. <br>
     *
     * @param map Map <br>
     * @param key 键 <br>
     * @param <K> <br>
     * @param <V> <br>
     * @return <br>
     */
    public static <K, V> String getMapString(Map<K, V> map, K key) {
        if (null == map) {
            return "";
        }
        Object value = map.getOrDefault(key, null);
        if (null == value) {
            return "";
        }
        else {
            String retString = ClassUtil.cast2String(value);
            return ifEmpty(retString);
        }
    }

    /**
     * Description: json字符串转义. <br>
     *
     * @param string 字符串 <br>
     * @return <br>
     */
    public static String escape4Json(String string) {
        return string
            .replaceAll("\\\\", "\\\\\\\\")
            .replaceAll("\"", "\\\\\"")
            .replaceAll("\r", "\\\\r")
            .replaceAll("\t", "\\\\t")
            .replaceAll("\n", "\\\\n");
    }

    /**
     * Description: json字符串反转义. <br>
     *
     * @param string 字符串 <br>
     * @return <br>
     */
    public static String unescape4Json(String string) {
        return string
            .replace("\\\\", "\\")
            .replaceAll("\\\\r", "\\r")
            .replaceAll("\\\\n", "\\n")
            .replaceAll("\\\\\"", "\"")
            .replaceAll("\\\\t", "\\t");
    }

    /**
     * Description: xml字符串转义. <br>
     *
     * @param string 字符串 <br>
     * @return <br>
     */
    public static String escape4XML(String string) {
        string = string
            .replaceAll("&", "&amp;")
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;")
            .replaceAll("'", "&apos;")
            .replaceAll("\"", "&quot;");
        return string;
    }

    /**
     * Description: xml字符串反转义. <br>
     *
     * @param string 字符串 <br>
     * @return <br>
     */
    public static String unescape4XML(String string) {
        string = string
            .replaceAll("&amp;", "&")
            .replaceAll("&lt;", "<")
            .replaceAll("&gt;", ">")
            .replaceAll("&apos;", "'")
            .replaceAll("&quot;", "\"");
        return string;
    }

    public static String readFromInputStream(InputStream inputStream) {
        return readFromInputStream(inputStream, "utf-8");
    }

    public static String readFromInputStream(InputStream inputStream, String charSet) {
        return readFromInputStream(inputStream, 3072, charSet);
    }

    public static String readFromInputStream(InputStream inputStream, int bufferSize, String charSet) {
        try {
            StringBuilder stbr = new StringBuilder(bufferSize);
            char[] buffer = new char[bufferSize];
            InputStreamReader reader = StringUtil.isEmpty(charSet) ? new InputStreamReader(inputStream) : new InputStreamReader(inputStream, charSet);
            int index;
            while ((index = reader.read(buffer)) != -1) {
                stbr.append(buffer, 0, index);
                if (0 == inputStream.available()) {
                    break;
                }
            }
            return stbr.toString();
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
    }

    public static String getStringFromArray(String[] arr, int index, String defaultString) {
        if (null == arr || arr.length <= index) {
            return defaultString;
        }
        else {
            return arr[index];
        }
    }

    public static String getStringFromArray(String[] arr, int index) {
        return getStringFromArray(arr, index, "");
    }

    public static String cutStringByMaxBytesLength(String string, String charset, int maxBytesLength) {
        try {
            byte[] bytes = string.getBytes(charset);
            return new String(bytes, 0, Math.min(maxBytesLength, bytes.length), charset);
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
    }

    public static boolean checkExists(String input, String toBeCheck, String... toBeChecks) {
        if (null == input) {
            if (null == toBeCheck) return true;
            if (null == toBeChecks) return false;
            for (String s : toBeChecks) {
                if (null == s) {
                    return true;
                }
            }
            return false;
        }
        if (input.equals(toBeCheck)) return true;
        if (null == toBeChecks) return false;
        for (String s : toBeChecks) {
            if (input.equals(s)) {
                return true;
            }
        }
        return false;
    }

    public static byte[] getBytes(String input, String charset) {
        return getBytes(input, Charset.forName(charset));
    }

    public static byte[] getBytes(String input, Charset charset) {
        if (null == input) {
            return new byte[0];
        }
        return input.getBytes(charset);
    }

    public static String parseBytes(byte[] input, String charset) {
        return parseBytes(input, Charset.forName(charset));
    }

    public static String parseBytes(byte[] input, Charset charset) {
        if (null == input) {
            return null;
        }
        return new String(input, charset);
    }
}
